﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Net;
using System.IO;
using System.Threading;

namespace HostsCreator
{
    public partial class Form1 : Form
    {
        //使用HashSet记录备选列表，避免重复
        public static HashSet<string> Hosts = new HashSet<string>();

        public Form1()
        {
            InitializeComponent();
            webBrowser1.DocumentTitleChanged += new EventHandler(DocumentTitleChanged);
            this.FormClosing += new FormClosingEventHandler(Form1_FormClosing);
        }

        void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            Save();
            ProxyHook.Stop();
        }

        void NewWindow(object sender, CancelEventArgs e)
        {
            e.Cancel = true;
        }

        void DocumentTitleChanged(object sender, EventArgs e)
        {
            this.Text = webBrowser1.DocumentTitle + " - HostsCreator";
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                if (textBox2.Text.ToLower().StartsWith("http://"))
                    webBrowser1.Navigate(new Uri(textBox2.Text));
                else
                    webBrowser1.Navigate(new Uri("http://" + textBox2.Text));
            }
            catch (Exception) { }
        }

        private void LoadHosts()
        {
            try
            {
                using (StreamReader Reader = new StreamReader("Hosts.txt"))
                {
                    Hosts = new HashSet<string>(Reader.ReadToEnd().Split('\r'));
                    while (!Reader.EndOfStream)
                    {
                        Hosts.Add(Reader.ReadLine());
                    }
                }
            }
            catch (Exception) { }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            //排序域名并输出
            textBox1.Text = "";
            IEnumerable<string> Sorted = Hosts.OrderBy(x => x);
            foreach (string s in Sorted)
            {
                textBox1.Text += s + "\r\n";
            }
            Save();
        }

        private void Save()
        {
            File.Delete("Hosts.txt");
            using (StreamWriter Writer = new StreamWriter("Hosts.txt"))
            {
                IEnumerable<string> Sorted = Hosts.OrderBy(x => x);
                foreach (string s in Sorted)
                {
                    Writer.WriteLine(s);
                }
            }
        }

        private void Navigated(object sender, WebBrowserNavigatedEventArgs e)
        {
            textBox2.Text = webBrowser1.Url.ToString();
            WallTest.TestUri(e.Url);
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            LoadHosts();
            new Thread(ProxyHook.Start).Start();
        }
    }

     static class ProxyHook
    {
        private static HttpListener Listener = new HttpListener();
        private static bool _continue=true;

        public static void Start()
        {
            Listener.Prefixes.Add("http://+/");
            Listener.Start();
            while (_continue)
            {
                HttpListenerContext Context = Listener.GetContext();
                new Thread(new Worker(Context).Process).Start();
            }
        }

        public static void Stop()
        {
            _continue = false;
        }
    }

    class Worker
    {
        private HttpListenerContext _context;

        public Worker(HttpListenerContext Context)
        {
            this._context = Context;
        }

        public void Process()
        {
            try
            {
                HttpWebRequest.DefaultWebProxy = new WebProxy("127.0.0.1:8087");
                HttpWebRequest Request = (HttpWebRequest)HttpWebRequest.Create(_context.Request.Url);

                try
                {
                    Request.KeepAlive = _context.Request.KeepAlive;
                    Request.Method = _context.Request.HttpMethod;
                    Request.UserAgent = _context.Request.UserAgent;
                    Request.ProtocolVersion = _context.Request.ProtocolVersion;
                    Request.Host = _context.Request.Headers["Host"];
                    Request.Referer = _context.Request.Headers["Referer"];
                    Request.Accept = _context.Request.Headers["Accept"];
                    Request.Connection = _context.Request.Headers["Connection"];
                    Request.CookieContainer = new CookieContainer();
                    foreach (Cookie cookie in _context.Request.Cookies)
                    {
                        if (cookie.Domain == "")
                            cookie.Domain = _context.Request.Url.DnsSafeHost;
                        Request.CookieContainer.Add(cookie);
                    }
                }
                catch { }

                HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();
                _context.Response.ContentType = Response.ContentType;
                _context.Response.Cookies.Add(Response.Cookies);
                Response.GetResponseStream().CopyTo(_context.Response.OutputStream);
                _context.Response.StatusCode = (int)Response.StatusCode;
                _context.Response.StatusDescription = Response.StatusDescription;

                foreach (string name in Response.Headers.AllKeys)
                {
                    _context.Response.AddHeader(name, Response.Headers[name]);
                }
                _context.Response.Cookies.Add(Response.Cookies);
                _context.Response.Close();

                WallTest.TestUri(_context.Request.Url);
            }
            catch (WebException ex)
            {
                _context.Response.StatusCode = (int)((HttpWebResponse)ex.Response).StatusCode;
                _context.Response.StatusDescription = ((HttpWebResponse)ex.Response).StatusDescription;
                try
                {
                    ((HttpWebResponse)ex.Response).GetResponseStream().CopyTo(_context.Response.OutputStream);
                }
                catch { }
            }
            catch (HttpListenerException) { }
            catch (ArgumentException) { }
            finally
            {
                try
                {
                    _context.Response.Close();
                }
                catch { }
            }
        }
    }

    class WallTest
    {
        public static void TestUri(Uri Url)
        {
            string DestString = Url.DnsSafeHost;
            if (!Form1.Hosts.Contains(DestString))
            {
                //有 google 的直接加
                if (DestString.IndexOf("google") != -1
                    || DestString.IndexOf("youtube") != -1
                    || DestString.IndexOf("ytimg") != -1)
                    Form1.Hosts.Add(DestString);

                //尝试连接目标判断是否被墙
                HttpWebRequest WallTestRequest = (HttpWebRequest)HttpWebRequest.Create(Url);
                WallTestRequest.Proxy = GlobalProxySelection.GetEmptyWebProxy();
                WallTestRequest.BeginGetResponse(new AsyncCallback(WallTestCallback), WallTestRequest);
            }
        }

        private static void WallTestCallback(IAsyncResult Result)
        {
            //异步连接处理
            HttpWebRequest Request = (HttpWebRequest)Result.AsyncState;
            try
            {
                HttpWebResponse Response = (HttpWebResponse)Request.EndGetResponse(Result);
            }
            catch (System.Net.WebException ex)
            {
                //精确检测被墙，加入Hosts备选列表中
                if (ex.Status.HasFlag(WebExceptionStatus.SendFailure)
                    || ex.Status.HasFlag(WebExceptionStatus.ConnectFailure))
                    Form1.Hosts.Add(Request.RequestUri.Authority);
            }
        }
    }
}